♻️ refactor: own cross-runtime test infrastructure as @executablemd/test-support - #188
Merged
Conversation
packages/test-support kept the upstream @effectionx/bdd name long after it stopped being that package. The local code drives Deno, Node, and Bun, ships a cross-runtime expect, and is about to own the executable.md CLI launcher — none of which upstream does. Rename the package, declare its real dependencies, and migrate every import. @effectionx/test-adapter stays; it still supplies the Effection scope lifecycle. Test behavior is unchanged.
PR #188: ♻️ refactor: own cross-runtime test infrastructure as @executablemd/test-support119 files, +250 / -234 Scope🟡 484 lines changed. PRs under 400 receive more thorough review. 🟡 119 files changed. Are all changes related? 🟡 PR mixes config and source changes. Structural✅ No structural bloat detected. Slop✅ Slop indicators look low. Static Analysis✅ Oxlint found no issues. CorrectnessNo extraneous code patterns detected. |
DEC-004's update note named Node as the test runner. Tests run under Deno, Node, and Bun, with @executablemd/test-support binding each runner to one test surface.
taras
added a commit
that referenced
this pull request
Jul 28, 2026
Ten suites shelled out to the CLI with a hardcoded `deno run --allow-all packages/cli/src/deno.ts`. That pinned them to one runtime and hid whether the CLI works under the others — the npm package runs under Node, and the published bin is what users get. `@executablemd/test-support/launch` builds the command for whichever runtime is running the tests, using the entrypoint that runtime installs its `command` and `compile` providers from. It returns a command, never a process: seven suites spawn it and each consumes the process differently — `.expect()`, `.join()`, manual stdout streaming under four timeboxes, and one long-lived JSON-RPC peer — while three never spawn it at all, handing a base array to `API.Env.command` for the test-agent provider to launch. Detection belongs here rather than being tolerated: packages/test-support is the host-adapter boundary for tests, which rule 12 already exempts (#188). Node starts a fresh tsx process rather than reusing the running one, so a CLI subprocess does not inherit `--test` or the runner's loaders, and naming the tsconfig on the command line means no suite has to alter its environment. Suite-specific environments, timeouts and process handling are untouched: agent-cli isolates HOME, props-cli allowlists so an inherited XMD_PROPS_* cannot corrupt its precedence assertions, and the rest inherit deliberately. worker-lifecycle passed its whole command line as one interpolated string and now uses command + arguments form. XC4 asserted "the Deno entrypoint relaunches a worker"; it exercises whichever runtime is running, and says so. This makes the ten suites runtime-portable. Adding them to the derived Node and Bun scope is #144. Ran explicitly: 73 tests, 0 failures under Deno, Node and Bun.
This was referenced Jul 28, 2026
taras
added a commit
that referenced
this pull request
Jul 28, 2026
test-bun failed while test-node and test-deno passed, on one root cause: the Bun job runs `bun install` alone, and that links only the workspace packages the root manifest depends on. `@executablemd/test-support` was the sole entry, added by #188 for exactly this reason, so nothing else appeared under node_modules/@executablemd. Every failure followed from it. Eval blocks compile to `.xmd-eval/<uuid>.ts` beside node_modules and could not resolve `@executablemd/core`, which is why the provider, sample and agent suites reported ERROR in their rendered output rather than failing an assertion outright. It passed locally because pnpm links all nine, so the pnpm and Bun layouts disagreed and only CI ran the Bun one. Declaring the packages at the root makes `bun install` produce the same layout: verified by moving node_modules aside, running `bun install` alone, and getting all nine links and 1131 passing tests where the same layout previously failed 45. The old hand-picked `test:bun` list never reached these suites, so the gap existed before this branch and only became visible once the scope was derived.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
packages/test-supportwas still published to the workspace as@effectionx/bdd, but it has not been that package for a while. Upstream@effectionx/bddcovers Node and Deno, and owns neither assertions nor processlaunching. The local package drives Deno, Node, and Bun, ships a
cross-runtime
expect, and is about to own the executable.md CLI launcherarriving in #186. The borrowed name misrepresented what the code is and where
it comes from.
This separates repository-owned cross-runtime test infrastructure from the
upstream EffectionX BDD package, and gives the launcher in #186 a correctly
named home to land in. #186 is paused on this merging.
What changes
Before: tests imported
@effectionx/bdd/nodeand@effectionx/bdd/expect,resolving to a private local package that claimed the upstream identity and
declared only
expectas a dependency.After: tests import
@executablemd/test-support/bddand@executablemd/test-support/expect. The package declares its real directdependencies —
@effectionx/test-adapter,effection, andexpect— at theversions the root workspace already pins.
@effectionx/test-adapteris unchanged and still supplies the Effection scopelifecycle that
bdd.tsbuilds on. Only the BDD package identity is dropped.How it works
Each runtime resolves the two subpaths by a different mechanism, so all three
had to move together:
Bun resolves the package as a real
workspace:*dependency and reads thesubpaths from the package's own
exportsmap —bunfig.toml'stsconfig-overridedoes not provide module resolution.What must stay true
private: trueandcarries no
deno.json;scripts/build-npm.ts, theGenerateblock ofscripts/gen-publish-workflow.md, andscripts/lib/bump-version.tsall skipworkspace dirs without one. Verified:
deno task gen:publish-workflowregenerates
publish-packages.ymlbyte-identically (still 8 packages), anddeno task check:jsrsucceeds.bdd.tsandexpect.tshave no changes —no edits to BDD lifecycle, runtime detection, assertions, sanitizers, or the
public test API. Verified by the full Deno repository suite passing, and by
the currently configured Node and Bun suites passing.
How to verify it
All checks run against the CI-pinned toolchains (Deno 2.9.1, Bun 1.3.14):
deno task lint— 0 errorsdeno task check— 0 errorsdeno task test— 145 passed, 0 faileddeno task check:jsr—Success Dry run completepnpm install --frozen-lockfile— cleanpnpm exec tsc --project tsconfig.node.json --noEmit— 0 errorspnpm test:node— 305 passed, 0 failedbun install— reports no changes;bun run test:bun— 272 passed, 0 faileddeno task gen:publish-workflow— no driftdeno task testis the full repository suite.test:nodeandtest:bunnametheir test files explicitly, so they run a subset — deriving that scope is #144
and is not attempted here. What each proves is its own resolution path: a
missed
deno.json,tsconfig.node.json, orexportsentry fails thatruntime's tests with an unresolved-module error and nothing else. Every
migrated import is type-checked repository-wide by
deno task checkandtsc --project tsconfig.node.json --noEmit.git grep "@effectionx/bdd"returns nothing repository-wide.Scope
Included
./bdd/./expectexport keys.
scripts/tests/fixtures/(excluded from lint and typecheck, but linted by therule tests through the repository config).
deno.json,tsconfig.node.json, root devDependency.pnpm-lock.yaml,deno.lock,bun.lock.AGENTS.mdrule 12 — recordspackages/test-supportas the deliberatehost-adapter boundary for tests, exempt from the no-runtime-detection rule,
and notes that the Oxlint rule tracked by Route production host access through Env #156 carries the same path
exemption. The module detects the runtime on purpose; that is how one BDD
surface drives
@std/testing/bdd,node:test, andbun:test.specs/executable-mdx-spec.md— the spec describedbdd.tsas a"Deno-native BDD shim", which is wrong for a module driving three runtimes.
Now "Cross-runtime Effection BDD adapter". Contract maintenance the rename
forces.
packages/durable-streams/specs/DECISIONS.md— DEC-004 named the old package.Intentionally unchanged
bdd.tsandexpect.tsimplementations.@effectionx/test-adapter— still an upstream dependency, unchanged.launch.ts. ✅ test: launch the CLI through the host runtime #186 adds it after rebasing onto this.tsconfig.json— tested previously and it breaksdeno check.Generated or mechanical changes
intended behavior change. Reviewers can skim them.
pnpm-lock.yaml,deno.lock, andbun.lockcome from their own packagemanagers. Each diff is confined to the rename and the newly declared
dependencies — no unrelated re-resolution.
bun.lockwas generated with theCI-pinned Bun 1.3.14 rather than the local 1.3.10 so it converges with CI.
Scope confirmation